/*
 * Copyright (C) 2006-2010 - Frictional Games
 *
 * This file is part of Penumbra Overture.
 *
 * Penumbra Overture is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Penumbra Overture is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Penumbra Overture.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "Player.h"

#include "PlayerHelper.h"
#include "Init.h"
#include "GameEntity.h"
#include "MapHandler.h"
#include "PlayerMoveStates.h"

#include "PlayerState_InteractHaptX.h"
#include "PlayerState_MiscHaptX.h"
#include "PlayerState_WeaponHaptX.h"

#include "PlayerState_Interact.h"
#include "PlayerState_Misc.h"
#include "PlayerState_Weapon.h"

#include "ButtonHandler.h"
#include "Inventory.h"
#include "Notebook.h"
#include "SaveHandler.h"
#include "EffectHandler.h"

#include "HapticGameCamera.h"

#include "TriggerHandler.h"
#include "Triggers.h"

#include "GlobalInit.h"

//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayer::cPlayer(cInit *apInit)  : iUpdateable("Player")
{
	mpInit = apInit;

	mpScene = apInit->mpGame->GetScene();
	mpGraphics = apInit->mpGame->GetGraphics();
	mpGfxDrawer = mpGraphics->GetDrawer();
	mpResources = apInit->mpGame->GetResources();
    
	//Create and setup camera
	mpCamera = mpScene->CreateCamera3D(eCameraMoveMode_Walk);
	mpScene->SetCamera(mpCamera);
	
	//Get Debug variables
	mbShowHealth = mpInit->mpConfig->GetBool("Debug","ShowHealth",false);
	mbShowSoundsPlaying = mpInit->mpConfig->GetBool("Debug","ShowSoundsPlaying",false);

	mvSize.x = mpInit->mpGameConfig->GetFloat("Player","Width",1);
	mvSize.y = mpInit->mpGameConfig->GetFloat("Player","Height",1);
	mvSize.z = mvSize.x;

	mfCameraHeightAdd =  mpInit->mpGameConfig->GetFloat("Player","CameraHeightAdd",0);

	mfDefaultMass = mpInit->mpGameConfig->GetFloat("Player","Mass",1);
	mfMass = mfDefaultMass;

	mfCrouchHeight = mpInit->mpGameConfig->GetFloat("Player","CrouchHeight",1);;
	
	mfSpeedMul = 1.0f;

	mfHeadMoveSizeMul = 1.0f;
	mfHeadMoveSpeedMul = 1.0f;

	mItemFlash.SetUp(0.3f,1,0,1,1);

	//jump properties
	mbJumpButtonDown = false;
	mfJumpCount = 0;
	mfMaxJumpCount = mpInit->mpGameConfig->GetFloat("Player","MaxJumpCount",1);
	
	//Create and init player states
	mState = ePlayerState_Normal;
	
	mvStates.resize(ePlayerState_LastEnum);

	if(mpInit->mbHasHaptics)
	{
		mvStates[ePlayerState_Normal] = hplNew( cPlayerState_NormalHaptX, (mpInit,this) );
		mvStates[ePlayerState_Push] = hplNew( cPlayerState_PushHaptX, (mpInit,this) );
		mvStates[ePlayerState_Move] = hplNew( cPlayerState_MoveHaptX, (mpInit,this) );
		mvStates[ePlayerState_InteractMode] = hplNew( cPlayerState_InteractModeHaptX, (mpInit,this) );
		mvStates[ePlayerState_Grab] = hplNew( cPlayerState_GrabHaptX, (mpInit,this) );
		mvStates[ePlayerState_WeaponMelee] = hplNew( cPlayerState_WeaponMeleeHaptX, (mpInit,this) );
		mvStates[ePlayerState_UseItem] = hplNew( cPlayerState_UseItemHaptX, (mpInit,this) );
		mvStates[ePlayerState_Message] = hplNew( cPlayerState_MessageHaptX, (mpInit,this) );
		mvStates[ePlayerState_Throw] = hplNew( cPlayerState_ThrowHaptX, (mpInit,this) );
		mvStates[ePlayerState_Climb] = hplNew( cPlayerState_ClimbHaptX, (mpInit,this) );
	}
	else
	{
		mvStates[ePlayerState_Normal] = hplNew( cPlayerState_Normal, (mpInit,this) );
		mvStates[ePlayerState_Push] = hplNew( cPlayerState_Push, (mpInit,this) );
		mvStates[ePlayerState_Move] = hplNew( cPlayerState_Move, (mpInit,this) );
		mvStates[ePlayerState_InteractMode] = hplNew( cPlayerState_InteractMode, (mpInit,this) );
		mvStates[ePlayerState_Grab] = hplNew( cPlayerState_Grab, (mpInit,this) );
		mvStates[ePlayerState_WeaponMelee] = hplNew( cPlayerState_WeaponMelee, (mpInit,this) );
		mvStates[ePlayerState_UseItem] = hplNew( cPlayerState_UseItem, (mpInit,this) );
		mvStates[ePlayerState_Message] = hplNew( cPlayerState_Message, (mpInit,this) );
		mvStates[ePlayerState_Throw] = hplNew( cPlayerState_Throw, (mpInit,this) );
		mvStates[ePlayerState_Climb] = hplNew( cPlayerState_Climb, (mpInit,this) );
	}	
	
	//The max distance you can be from something to grab it.
	mfMaxGrabDist =  mpInit->mpGameConfig->GetFloat("Player","MaxGrabDist",0);
	
	//The max distance you can be from something to move it.
	mfMaxMoveDist =  mpInit->mpGameConfig->GetFloat("Player","MaxMoveDist",0);

	//The max distance you can be from something to push it.
	mfMaxPushDist =  mpInit->mpGameConfig->GetFloat("Player","MaxPushDist",0);
	//The maximum speed you can push something with
	mfMaxPushSpeed =  mpInit->mpGameConfig->GetFloat("Player","MaxPushSpeed",0);
	mvMaxPushHeadMovement = cVector2f(cMath::ToRad(5),cMath::ToRad(15));
	mvMinPushHeadMovement = cVector2f(cMath::ToRad(-5),cMath::ToRad(-10));

	//This is the maximum distance on which an item can be used.
	mfMaxUseItemDist =  mpInit->mpGameConfig->GetFloat("Player","MaxUseItemDist",0);

	//Set the maximum time the jumpbutton can be held and make the jump longer.
	mfJumpCount = 0;
	mfMaxJumpCount = mpInit->mpGameConfig->GetFloat("Player","MaxJumpCount",1);

	//The border that decides when the mouse moves the screen in interact mode.
	mvInteractMoveBorder = cVector2f(130,95);

	//Create head movement
	mpHeadMove = hplNew( cPlayerHeadMove,(this) );

	//create damage effect
	mpDamage = hplNew( cPlayerDamage,(mpInit) );

	//Create death effect
	mpDeath = hplNew( cPlayerDeath,(mpInit) );

	//Create flashlight
	mpFlashLight = hplNew( cPlayerFlashLight,(mpInit) );
	
	//Create Glowstick
	mpGlowStick = hplNew( cPlayerGlowStick,(mpInit) );

	//Create Flare
	mpFlare = hplNew( cPlayerFlare,(mpInit) );

	//Create leaner
	mpLean = hplNew( cPlayerLean,(mpInit,this) );
	
	//Create ear ringer
	mpEarRing = hplNew( cPlayerEarRing,(mpInit,this) );

	//Health
	mpHealth = hplNew( cPlayerHealth,(mpInit) );
	
	//NOise Filter
	mpNoiseFilter = hplNew( cPlayerNoiseFilter,(mpInit) );

	//Fear Filter
	mpFearFilter = hplNew( cPlayerFearFilter,(mpInit) );

	//Look at
	mpLookAt = hplNew( cPlayerLookAt,(this) );

	//Hidden
	mpHidden = hplNew( cPlayerHidden,(mpInit) );
	
	//Create ray callbacks
	mpGroundRayCallback = hplNew( cPlayerGroundRayCallback,() );
	mpPickRayCallback = hplNew( cPlayerPickRayCallback,() );

	//Create body callback
	mpBodyCallback = hplNew( cPlayerBodyCallback,(this) );

	//Load font
	mpFont = mpResources->GetFontManager()->CreateFontData("verdana.fnt");

	//Create and init move states
	//This must be called after head move is created!
	mMoveState = ePlayerMoveState_Walk;
	
	mvMoveStates.resize(ePlayerMoveState_LastEnum);
	mvMoveStates[ePlayerMoveState_Walk] = hplNew( cPlayerMoveState_Walk,(this,mpInit) );
	mvMoveStates[ePlayerMoveState_Run] = hplNew( cPlayerMoveState_Run,(this,mpInit) );
	mvMoveStates[ePlayerMoveState_Still] = hplNew( cPlayerMoveState_Still,(this,mpInit) );
	mvMoveStates[ePlayerMoveState_Jump] = hplNew( cPlayerMoveState_Jump,(this,mpInit) );
	mvMoveStates[ePlayerMoveState_Crouch] = hplNew( cPlayerMoveState_Crouch,(this,mpInit) );


	/////////////////////////
	//Create player gui stuff

	//Cross hair
	mCrossHairState = eCrossHairState_None;
	mvCrossHairPos = cVector2f(400, 300);
	
	mvCrossHairs.resize(eCrossHairState_LastEnum);

	for(size_t i=0; i < mvCrossHairs.size(); i++) mvCrossHairs[i] = NULL;

	mvCrossHairs[eCrossHairState_Inactive] = mpGfxDrawer->CreateGfxObject("player_crosshair_inactive","diffalpha2d");
	mvCrossHairs[eCrossHairState_Active] = mpGfxDrawer->CreateGfxObject("player_crosshair_active","diffalpha2d");
	mvCrossHairs[eCrossHairState_Invalid] = mpGfxDrawer->CreateGfxObject("player_crosshair_invalid","diffalpha2d");
	mvCrossHairs[eCrossHairState_Grab] = mpGfxDrawer->CreateGfxObject("player_crosshair_grab","diffalpha2d");
	mvCrossHairs[eCrossHairState_Examine] = mpGfxDrawer->CreateGfxObject("player_crosshair_examine","diffalpha2d");
	mvCrossHairs[eCrossHairState_Pointer] = mpGfxDrawer->CreateGfxObject("player_crosshair_pointer","diffalpha2d");
	mvCrossHairs[eCrossHairState_DoorLink] = mpGfxDrawer->CreateGfxObject("player_crosshair_doorlink","diffalpha2d");
	mvCrossHairs[eCrossHairState_PickUp] = mpGfxDrawer->CreateGfxObject("player_crosshair_pickup","diffalpha2d");
	mvCrossHairs[eCrossHairState_Ladder] = mpGfxDrawer->CreateGfxObject("player_crosshair_ladder","diffalpha2d");
	mvCrossHairs[eCrossHairState_Cross] = mpGfxDrawer->CreateGfxObject("player_crosshair_cross","diffalpha2d");

	///////////////////////////////
	//Haptic Init
	if(mpInit->mbHasHaptics)
	{
		mpLowLevelHaptic = mpInit->mpGame->GetHaptic()->GetLowLevel();

		mpLowLevelHaptic->SetCamera(mpCamera);
		mpLowLevelHaptic->SetCameraOffset(cVector3f(0,0.7f,mpInit->mfHapticOffsetZ));

		mpHapticCamera = hplNew( cHapticGameCamera, (mpInit,this) );

		mpDamageForce = mpInit->mpGame->GetHaptic()->GetLowLevel()->CreateSinusWaveForce(
																	cVector3f(0,1,0),0.1f,3);

		mpDamageDirForce = mpLowLevelHaptic->CreateImpulseForce(0);
		mpDamageDirForce->SetActive(false);

		mpHapticCamera->SetInteractModeCameraSpeed(mpInit->mpConfig->GetFloat("Haptics","InteractModeCameraSpeed",0.5f));
		mpHapticCamera->SetActionModeCameraSpeed(mpInit->mpConfig->GetFloat("Haptics","ActionModeCameraSpeed",1.0f));
	}

	//Set up variable values
	Reset();
}

//-----------------------------------------------------------------------

cPlayer::~cPlayer(void)
{
	if(mpInit->mbHasHaptics) hplDelete( mpHapticCamera );
	hplDelete( mpGroundRayCallback);
	hplDelete( mpPickRayCallback);
	hplDelete( mpHeadMove);
	hplDelete( mpBodyCallback);
	hplDelete( mpDamage);
	hplDelete( mpDeath);
	hplDelete( mpFlashLight);
	hplDelete( mpLean);
	hplDelete( mpEarRing);
	hplDelete( mpGlowStick);
	hplDelete( mpFlare);
	hplDelete( mpHealth);
	hplDelete( mpNoiseFilter);
	hplDelete( mpFearFilter);
	hplDelete( mpLookAt);
	hplDelete( mpHidden);

	/*mpInit->mpConfig->SetFloat("Game","PlayerWidth",mvSize.x);
	mpInit->mpConfig->SetFloat("Game","PlayerHeight",mvSize.y);
	mpInit->mpConfig->SetFloat("Game","PlayerCrouchHeight",mfCrouchHeight);*/

	mpInit->mpConfig->SetBool("Debug","ShowHealth",mbShowHealth);
	mpInit->mpConfig->SetBool("Debug","ShowSoundsPlaying",mbShowSoundsPlaying);
	
	STLDeleteAll(mvMoveStates);
	STLDeleteAll(mvStates);

	for(size_t i=0; i < mvCrossHairs.size(); i++)
		if(mvCrossHairs[i]) mpGfxDrawer->DestroyGfxObject(mvCrossHairs[i]);

	STLMapDeleteAll(m_mapCollideCallbacks);
}

//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS - PROPERTIES
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

void cPlayer::SetMass(float afX)
{
	mfMass = afX;
	if(mpCharBody)mpCharBody->SetMass(afX);
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS - ACTION
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

void cPlayer::SetActive(bool abActive)
{
	mbActive = abActive;

	if(mbActive==false)
	{
		ChangeState(ePlayerState_Normal);
	}
}

//-----------------------------------------------------------------------

void cPlayer::SetPower(float afX)
{
	mfPower = afX;
}

void cPlayer::AddPower(float afX)
{
	mfPower += afX;
	if(mfPower > 100) mfPower =100;
	if(mfPower < 0) mfPower =0;
}

//-----------------------------------------------------------------------

void cPlayer::SetPrevMoveState(ePlayerMoveState aState)
{
	mvMoveStates[mMoveState]->mPrevMoveState = aState;
}

//-----------------------------------------------------------------------

void cPlayer::SetStartPos(const tString& asName)
{
	ChangeState(ePlayerState_Normal);	

	cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
	if(pWorld)
	{
		cStartPosEntity *pStart = pWorld->GetStartPosEntity(asName);
		if(pStart==NULL)
		{
			Warning("Couldn't find start position '%s'\n",asName.c_str());

			pStart = pWorld->GetFirstStartPosEntity();
		}

		cVector3f vPosition(0,0,0), vCamRotation(0,0,0),vBodyRotation(0,0,0);

		if(pStart)
		{
			vPosition = pStart->GetWorldMatrix().GetTranslation();

			cMatrixf mtxInv = cMath::MatrixInverse(pStart->GetWorldMatrix());
			vCamRotation = cMath::GetAngleFromPoints3D(cVector3f(0,0,0), mtxInv.GetForward()*-1);
			vBodyRotation = cMath::GetAngleFromPoints3D(cVector3f(0,0,0), mtxInv.GetForward());
		}

		//Set position of the player, this should include body and stuff:
		//mpCamera->SetPosition(vPosition + cVector3f(0,mvSize.y/2.0f,0));
		mpCharBody->SetPosition(vPosition + cVector3f(0,mpCharBody->GetSize().y/2.0f,0));
		//mpCharBody->SetPosition(cVector3f(0,0.43f,0));
		mpCamera->SetYaw(vCamRotation.y);
		mpCamera->SetPitch(vCamRotation.x);
	}
}


//-----------------------------------------------------------------------

void cPlayer::ChangeMoveState(ePlayerMoveState aState, bool abSetHeadHeightDirectly)
{
	if(mMoveState == aState) return;

	//Log("Change movestate from %d to: %d\n",(int)mMoveState,(int)aState);
	
	ePlayerMoveState PrevState = mMoveState;
	mMoveState = aState;
	
	mvMoveStates[aState]->InitState(mvMoveStates[PrevState]);

	if(abSetHeadHeightDirectly)
	{
		SetHeightAdd(mvMoveStates[aState]->mfHeightAdd);
	}
}

//-----------------------------------------------------------------------

void cPlayer::FootStep(float afMul, const tString &asType, bool abSkipCount)
{
	if(mlGroundCount<=0 && abSkipCount==false) return;

	iPhysicsMaterial *pMaterial = mpGroundRayCallback->mpMaterial;
	if(pMaterial==NULL) return;

	cSurfaceData *pData = pMaterial->GetSurfaceData();
	if(pData==NULL) {
		Error("No surface data in material '%s'!\n",pMaterial->GetName().c_str());
		return;
	}

	tString sMatStepType = pMaterial->GetSurfaceData()->GetStepType();
	if(sMatStepType == "")return;

	const tString& sType = asType!="" ? asType : mvMoveStates[mMoveState]->msStepType;

	tString sSoundName = "player_step_" + sType + "_" + sMatStepType;
	
	cResources *pResources = gpInit->mpGame->GetResources();

	cSoundEntityData *pSoundData = pResources->GetSoundEntityManager()->CreateSoundEntity(sSoundName);
	if(pSoundData)
	{
		cSoundHandler *pSoundHandler = gpInit->mpGame->GetSound()->GetSoundHandler();
        
		pSoundHandler->PlayGui(	pSoundData->GetMainSoundName(),false,
								afMul * pSoundData->GetVolume());

		cGameTrigger_Sound *pSound = hplNew( cGameTrigger_Sound, () );
		pSound->mpSound = pSoundData;
		mpInit->mpTriggerHandler->Add(pSound, eGameTriggerType_Sound,
						mpCharBody->GetFeetPosition() + cVector3f(0,0.2f,0),
						10, 1.0f/60.0f, pSoundData->GetMaxDistance());
	}

	/*cWorld3D *pWorld = mpScene->GetWorld3D();

	cSoundEntity *pSound = pWorld->CreateSoundEntity("Step",sSoundName,true);
	if(pSound)
	{
		pSound->SetVolume(afMul * pSound->GetVolume());
		pSound->SetPosition(cVector3f(0,0.2f,0.4f));
		
		mFeetNode.AddEntity(pSound);
		mFeetNode.SetPosition(mFeetNode.GetLocalPosition());
	}*/
}

//-----------------------------------------------------------------------

void cPlayer::ChangeState(ePlayerState aState)
{
	if(aState == mState) return;

	//Log("State %d --> %d\n",(int)mState, (int)aState);

	mvStates[aState]->InitState(mvStates[mState]);

	mState = aState;
}

//-----------------------------------------------------------------------

bool cPlayer::AddCrossHairPos(const cVector2f& avPos)
{ 
	bool abEdge = false; 

	mvCrossHairPos += avPos;
	if(mvCrossHairPos.x <mvInteractMoveBorder.x){
		mvCrossHairPos.x =mvInteractMoveBorder.x;
		abEdge = true;
	}
	if(mvCrossHairPos.y <mvInteractMoveBorder.y){
		mvCrossHairPos.y =mvInteractMoveBorder.y;
		abEdge = true;
	}
	if(mvCrossHairPos.x > (799 - mvInteractMoveBorder.x)){
		mvCrossHairPos.x =(799 - mvInteractMoveBorder.x);
		abEdge = true;
	}
	if(mvCrossHairPos.y > (599 - mvInteractMoveBorder.y)){
		mvCrossHairPos.y =(599 - mvInteractMoveBorder.y);
		abEdge = true;
	}

	return abEdge;
}

//-----------------------------------------------------------------------

void cPlayer::AddCollideScript(eGameCollideScriptType aType,const tString &asFunc, const tString &asEntity)
{
	cGameCollideScript *pCallback;

	//Check if the function already exist
	tGameCollideScriptMapIt it = m_mapCollideCallbacks.find(asEntity);
	if(it != m_mapCollideCallbacks.end())
	{
		pCallback = it->second;	
	}
	else
	{
		pCallback = hplNew( cGameCollideScript, () );

		//Get the entity
		iGameEntity *pEntity = mpInit->mpMapHandler->GetGameEntity(asEntity);
		if(pEntity==NULL)
		{
			Warning("Couldn't find entity '%s'\n",asEntity.c_str());
			hplDelete( pCallback );
			return;
		}

		//Set the entity
		pCallback->mpEntity = pEntity;

		//Add to container
		m_mapCollideCallbacks.insert(tGameCollideScriptMap::value_type(asEntity,pCallback));
	}

	pCallback->msFuncName[aType] = asFunc;	
}

//-----------------------------------------------------------------------

void cPlayer::RemoveCollideScriptWithChildEntity(iGameEntity *apEntity)
{
	tGameCollideScriptMapIt it = m_mapCollideCallbacks.begin();
	for(; it != m_mapCollideCallbacks.end(); )
	{
		cGameCollideScript *pCallback = it->second;
		tGameCollideScriptMapIt currentIt = it;
		++it;
		
		if(pCallback && pCallback->mpEntity == apEntity)
		{
			if(mbUpdatingCollisionCallbacks)
			{
				pCallback->mbDeleteMe = true;
			}
			else
			{
				hplDelete( pCallback );
				m_mapCollideCallbacks.erase(currentIt);
			}
		}
	}
}

//-----------------------------------------------------------------------

void cPlayer::RemoveCollideScript(eGameCollideScriptType aType,const tString &asEntity)
{
	tGameCollideScriptMapIt it = m_mapCollideCallbacks.find(asEntity);
	if(it != m_mapCollideCallbacks.end())
	{
		cGameCollideScript *pCallback = it->second;

		pCallback->msFuncName[aType] = "";
		//if there are no functions left, erase
		if(pCallback->msFuncName[0]=="" && pCallback->msFuncName[1]=="" && pCallback->msFuncName[2]=="")
		{
			if(mbUpdatingCollisionCallbacks)
			{
				pCallback->mbDeleteMe = true;
			}
			else
			{
				hplDelete( pCallback );
				m_mapCollideCallbacks.erase(it);
			}
		}
		
	}
	else
	{
		Warning("Entity '%s' callback doesn't exist in 'Player'\n",asEntity.c_str());
	}
}

//-----------------------------------------------------------------------

void cPlayer::ClearCollideScripts()
{
	STLMapDeleteAll(m_mapCollideCallbacks);
}

//-----------------------------------------------------------------------

void cPlayer::SetSpeedMul(float afSpeedMul)
{
	mfSpeedMul = afSpeedMul;

	mvMoveStates[mMoveState]->SetupBody();
}

//-----------------------------------------------------------------------

void cPlayer::SetHealthSpeedMul(float afHealthSpeedMul)
{
	mfHealthSpeedMul = afHealthSpeedMul;

	mvMoveStates[mMoveState]->SetupBody();
}


//-----------------------------------------------------------------------

void cPlayer::SetHealth(float afX)
{
	mfHealth = afX;
	
	if(mfHealth>100)
	{
		mfHealth = 100;
	}
	else if(mfHealth <= 0)
	{
		mpDeath->Start();
	}
}

void cPlayer::AddHealth(float afX)
{
	SetHealth(mfHealth + afX);
}

void cPlayer::Damage(float afDamage, ePlayerDamageType aType)
{
	if(afDamage <= 0) return;
	if(mpInit->mpMapHandler->IsChangingMap()) return;
	if(mfHealth <=0) return;

	if(mpInit->mDifficulty== eGameDifficulty_Easy) afDamage /= 2.0f;
	if(mpInit->mDifficulty== eGameDifficulty_Hard) afDamage *= 2.0f;

	if(mpInit->mbHasHaptics) afDamage /= 4.0f;

	if(mpDeath->IsActive()) return;
	
	float fSize =0.5f;
	if(afDamage> 10) fSize = 1.5f;
	if(afDamage> 20) fSize = 2.0f;
	if(afDamage> 50) fSize = 3.0f;
	if(afDamage> 80) fSize = 4.0f;
	mpDamage->Start(fSize,aType);

	AddHealth(-afDamage);

	if(mpInit->mbHasHaptics && aType == ePlayerDamageType_BloodSplash)
	{
		if(mbDamageFromPos)
		{
			mbDamageFromPos = false;

			cVector3f vDir = cMath::Vector3Normalize(mpCharBody->GetPosition() - mvDamagePos);

			if(mpDamageDirForce->IsActive()) mpDamageDirForce->SetActive(false);
			mpDamageDirForce->SetActive(true);
			
			cMatrixf mtxProxy = cMath::MatrixRotate(cVector3f(-mpCamera->GetPitch(),-mpCamera->GetYaw(),
													-mpCamera->GetRoll()),eEulerRotationOrder_YXZ);
			vDir = cMath::MatrixMul(mtxProxy,vDir);
			
			mpDamageDirForce->SetForce(vDir * fSize * 2.2f);
			mpDamageDirForce->SetTimeControl(false,0.2f,0.5f,0.0f,0.15f);
		}
		else
		{
			mpDamageForce->SetAmp(fSize * 0.15f);

			if(mpDamageForce->IsActive())	mpDamageForce->SetActive(false);
			mpDamageForce->SetActive(true);
			mpDamageForce->SetTimeControl(false,0.3f,0.0f,0.05f,0.25f);
		}
	}
}

//-----------------------------------------------------------------------

bool cPlayer::IsDead()
{
	return mpDeath->IsActive();
}

//-----------------------------------------------------------------------

iPhysicsBody* cPlayer::GetPickedBody()
{ 
	return mpPickRayCallback->mpPickedBody;
}
void cPlayer::SetPickedBody(iPhysicsBody* apBody)
{
	mpPickRayCallback->mpPickedBody = apBody;
}

//-----------------------------------------------------------------------

float cPlayer::GetPickedDist()
{ 
	//return mpPickRayCallback->mfPickedDist;
	return cMath::Vector3Dist(mpCharBody->GetPosition(),mpPickRayCallback->mvPickedPos);
}

const cVector3f& cPlayer::GetPickedPos()
{ 
	return mpPickRayCallback->mvPickedPos;
}
cPlayerPickRayCallback* cPlayer::GetPickRay()
{ 
	return mpPickRayCallback;
}


//-----------------------------------------------------------------------

void cPlayer::DestroyWorldObjects()
{
	//Body
	if(mpCharBody) mpScene->GetWorld3D()->GetPhysicsWorld()->DestroyCharacterBody(mpCharBody);
	
	//mpFlashLight->Destroy();
	//mpGlowStick->Destroy();
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS - INTERACTIONS
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

void cPlayer::MoveForwards(float afMul,float afTimeStep)
{
	if(mvStates[mState]->OnMoveForwards(afMul,afTimeStep))
	{
		//Only move if on ground
		if(mlGroundCount<=0 || afMul==0) return;

		mpCharBody->Move(eCharDir_Forward,afMul,afTimeStep);
		mbMoving = true;
		mvMoveStates[mMoveState]->Start();
	}
}

//-----------------------------------------------------------------------


void cPlayer::MoveSideways(float afMul,float afTimeStep)
{
	if(mvStates[mState]->OnMoveSideways(afMul,afTimeStep))
	{
		//Only move if on ground
		if(mlGroundCount<=0 || afMul==0) return;

		mpCharBody->Move(eCharDir_Right,afMul,afTimeStep);
		mbMoving = true;
		mvMoveStates[mMoveState]->Start();
	}
}


//-----------------------------------------------------------------------

void cPlayer::Lean(float afMul, float afTimeStep)
{
	mpLean->Lean(afMul,afTimeStep);
}

//-----------------------------------------------------------------------


void cPlayer::AddYaw(float afVal)
{
	if(mvStates[mState]->OnAddYaw(afVal))
	{
		mpCamera->AddYaw(-afVal * mfLookSpeed);
		mpCharBody->SetYaw(mpCamera->GetYaw());
	}
}

//-----------------------------------------------------------------------


void cPlayer::AddPitch(float afVal)
{
	if(mvStates[mState]->OnAddPitch(afVal))
	{
		float fInvert = mpInit->mpButtonHandler->GetInvertMouseY() ? -1.0f : 1.0f;
		mpCamera->AddPitch(-afVal * mfLookSpeed * fInvert);
	}
}

//-----------------------------------------------------------------------

void cPlayer::StartInteract()
{
	mvStates[mState]->OnStartInteract();
}

void cPlayer::StopInteract()
{
	mvStates[mState]->OnStopInteract();
}


//-----------------------------------------------------------------------

void cPlayer::StartExamine()
{
	mvStates[mState]->OnStartExamine();
}

void cPlayer::StopExamine()
{
	mvStates[mState]->OnStopExamine();
}


//-----------------------------------------------------------------------

void cPlayer::StartHolster()
{
	mvStates[mState]->OnStartHolster();
}

//-----------------------------------------------------------------------

void cPlayer::Jump()
{
	if(mvStates[mState]->OnJump() && mlGroundCount >0)
	{
		if(mvMoveStates[mMoveState]->mType != ePlayerMoveState_Jump)
		{
			ChangeMoveState(ePlayerMoveState_Jump);
		}
	}
	mfJumpCount = 0;
}

void cPlayer::SetJumpButtonDown(bool abX)
{
	mbJumpButtonDown = abX;

	if(mbJumpButtonDown)
	{
		mfJumpCount += mpInit->mpGame->GetStepSize();
	}
	else
	{
		mfJumpCount = mfMaxJumpCount;
	}
}

//-----------------------------------------------------------------------


void cPlayer::StartRun()
{
	mvStates[mState]->OnStartRun();
}

//-----------------------------------------------------------------------

void cPlayer::StopRun()
{
	mvStates[mState]->OnStopRun();
}

//-----------------------------------------------------------------------

void cPlayer::StartCrouch()
{
	mvStates[mState]->OnStartCrouch();
}

//-----------------------------------------------------------------------

void cPlayer::StopCrouch()
{
	mvStates[mState]->OnStopCrouch();
}

//-----------------------------------------------------------------------

void cPlayer::StartInteractMode()
{
	mvStates[mState]->OnStartInteractMode();
}

//-----------------------------------------------------------------------

void cPlayer::StartInventory()
{
	if(mvStates[mState]->OnStartInventory())
	{
		mpInit->mpInventory->SetActive(true);
	}
}

//-----------------------------------------------------------------------

void cPlayer::StartInventoryShortCut(int alNum)
{
	if(mvStates[mState]->OnStartInventoryShortCut(alNum))
	{
		mpInit->mpInventory->OnShortcutDown(alNum);
	}
}

//-----------------------------------------------------------------------

void cPlayer::StartFlashLightButton()
{
	if(mpInit->mpInventory->GetItem("flashlight")!=NULL)
	{
		mpFlashLight->SetActive(!mpFlashLight->IsActive());

		if(mpFlashLight->IsActive())
		{
			mpGlowStick->SetActive(false);
			mpFlare->SetActive(false);
		}
	}
}

void cPlayer::StartGlowStickButton()
{
	if(mpInit->mpInventory->GetItem("glowstick")!=NULL ||
		mpInit->mpInventory->GetItem("glowst1")!=NULL)
	{
		mpGlowStick->SetActive(!mpGlowStick->IsActive());

		if(mpGlowStick->IsActive())
		{
			mpFlashLight->SetActive(false);
			mpFlare->SetActive(false);
		}
	}
}

//-----------------------------------------------------------------------


//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS - EVENTS
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

void cPlayer::OnWorldLoad()
{	
	/////////////////////////////////////////////////////////
	// Create body
	mpCharBody = mpScene->GetWorld3D()->GetPhysicsWorld()->CreateCharacterBody("Player", mvSize);
	
	mpCharBody->SetCamera(mpCamera);
	mpCharBody->SetMass(mfMass);
	//mpCamera->SetPosition(cVector3f(1,1.2f,-2));
	
	mpCharBody->SetCameraPosAdd(cVector3f(0,mfCameraHeightAdd,0));
	mpCharBody->SetCameraSmoothPosNum(6);
	mpCharBody->SetCallback(mpBodyCallback);
	
	mpCharBody->SetMaxGravitySpeed(40.0f);
	mpCharBody->SetCustomGravityActive(true);
	mpCharBody->SetCustomGravity(cVector3f(0,-18.0f,0));

	mpCharBody->SetMaxPushMass(mpInit->mpGameConfig->GetFloat("Player","MaxPushMass",1));
	mpCharBody->SetPushForce(mpInit->mpGameConfig->GetFloat("Player","PushForce",1));

	mpCharBody->SetMaxStepSize(mpInit->mpGameConfig->GetFloat("Player","MaxStepSize",0));
	mpCharBody->SetStepClimbSpeed(mpInit->mpGameConfig->GetFloat("Player","StepClimbSpeed",0));
	mpCharBody->SetClimbForwardMul(1.75f);
	mpCharBody->SetClimbHeightAdd(0.02f);
	mpCharBody->SetAccurateClimbing(true);

	mpCharBody->SetGroundFriction(mpInit->mpGameConfig->GetFloat("Player","GroundFriction",0));
	mpCharBody->SetAirFriction(mpInit->mpGameConfig->GetFloat("Player","AirFriction",0));
	
	//Add the crouch size
	mpCharBody->AddExtraSize(cVector3f(mvSize.x, mfCrouchHeight,mvSize.z));

	//Set so it is not saved:
	mpCharBody->SetIsSaved(false);
	mpCharBody->GetExtraBody(0)->SetIsSaved(false);
	mpCharBody->GetExtraBody(1)->SetIsSaved(false);
	
	mvMoveStates[mMoveState]->EnterState(NULL);
	mvMoveStates[mMoveState]->Start();
	
    mpFlashLight->OnWorldLoad();
	//mpGlowStick->OnWorldLoad();
	mpFlare->OnWorldLoad();
	mpHidden->OnWorldLoad();
	mpLean->OnWorldLoad();
	if(mpInit->mbHasHaptics) mpHapticCamera->OnWorldLoad();
}

//-----------------------------------------------------------------------

void cPlayer::OnWorldExit()
{
	DestroyWorldObjects();
	
	mpGroundRayCallback->OnWorldExit();
	mpPickRayCallback->OnWorldExit();
	mpHidden->OnWorldExit();
	if(mpInit->mbHasHaptics) mpHapticCamera->OnWorldExit();
}

//-----------------------------------------------------------------------


void cPlayer::OnStart()
{

}

//-----------------------------------------------------------------------

static inline tString GetCollideCommand(const tString &asFuncName,const tString &asParent, 
										const tString &asChild)
{
	return asFuncName + "(\"" + asParent+"\", \""+asChild+"\")";
}

class cTempCheckProxy : public iPhysicsRayCallback
{
public:
	bool mbCollided;
	bool OnIntersect(iPhysicsBody *pBody,cPhysicsRayParams *apParams)
	{
		//if(apParams->mfT <0) return true;
		if(pBody->GetCollide()==false || pBody->IsCharacter()) return true;

		mbCollided = true;
		return false;
	}
};

cTempCheckProxy gTempCheckProxy;

void cPlayer::Update(float afTimeStep)
{
	cSystem *pSystem = mpInit->mpGame->GetSystem();
	unsigned int lTime = pSystem->GetLowLevel()->GetTime();	
	iPhysicsWorld *pPhysicsWorld = mpScene->GetWorld3D()->GetPhysicsWorld();

	/////////////////////////////////////
	// HaptX camera
	if(cHaptic::GetIsUsed())
	{
		mpHapticCamera->Update(afTimeStep);
	}

	
	//LogUpdate("  Death\n");
	////////////////////////////////////////
	// Make sure player is dead if he should be
	if(mfHealth <=0 && mpDeath->IsActive()==false)
	{
		mpDeath->Start();
	}

	
	////////////////////////////////////////
	// Update Node and Footstep sounds
	/*//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();			
	//LogUpdate("  Footstep sounds\n");
	cMatrixf mtxChar = mpInit->mpGame->GetSound()->GetLowLevel()->GetListenerMatrix();
	mtxChar.SetTranslation(mtxChar.GetTranslation() - 
							cVector3f(0,mpCharBody->GetSize().y/2,0));
	mFeetNode.SetMatrix(mtxChar);
	cSoundHandler *pSoundHandler = mpInit->mpGame->GetSound()->GetSoundHandler();
	cEntityIterator entIt = mFeetNode.GetEntityInterator();
	while(entIt.HasNext())
	{
		cSoundEntity *pSound = static_cast<cSoundEntity*>(entIt.Next());
		cSoundEntry *pEntry = pSound->GetSoundEntry(eSoundEntityType_Main);
		if(	pEntry && pSoundHandler->IsValid(pEntry->mpSound))
		{
			pEntry->mpSound->SetPosition(pSound->GetWorldPosition());
		}
	}
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);*/
	
	lTime = pSystem->GetLowLevel()->GetTime();			
	//LogUpdate("  misc\n");
	//////////////////////
	//Reset roll
	mpInit->mpPlayer->GetCamera()->SetRoll(0);

	/////////////////////////////////////////////////
	// Misc
	mItemFlash.Update(afTimeStep);

	/////////////////////////////////////////////////
	// Damage
	mpDamage->Update(afTimeStep);

	/////////////////////////////////////////////////
	// Death
	mpDeath->Update(afTimeStep);
	
	/////////////////////////////////////////////////
	// Flashlight
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();			
	//LogUpdate("  flashlight");
	mpFlashLight->Update(afTimeStep);
	
	/////////////////////////////////////////////////
	//Glowstick
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();			
	//LogUpdate("  glowstick\n");
	mpGlowStick->Update(afTimeStep);

	/////////////////////////////////////////////////
	//Flare
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();			
	//LogUpdate("  flare\n");
	mpFlare->Update(afTimeStep);

	/////////////////////////////////////////////////
	// Lean
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();
	//LogUpdate("  more misc\n");
	mpLean->Update(afTimeStep);

	/////////////////////////////////////////////////
	// Ear ring
	mpEarRing->Update(afTimeStep);
	
	//////////////////////////////////////////////////
	// health
	mpHealth->Update(afTimeStep);
	
	////////////////////////////////////////
	// Noise filter
	mpNoiseFilter->Update(afTimeStep);

	////////////////////////////////////////
	// Fear filter
	mpFearFilter->Update(afTimeStep);
	
	////////////////////////////////////////
	// Look at
	mpLookAt->Update(afTimeStep);

	////////////////////////////////////////
	// Hidden
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();
	//LogUpdate("  hidden\n");
	mpHidden->Update(afTimeStep);
	
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();
	//LogUpdate("  collide scripts\n");
	/////////////////////////////////////////////////
	// Collide script
	pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
	cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
	cCollideData collideData;
	collideData.SetMaxSize(1);

	mbUpdatingCollisionCallbacks = true;
	tGameCollideScriptMapIt CollideIt = m_mapCollideCallbacks.begin();
	for(; CollideIt != m_mapCollideCallbacks.end(); ++CollideIt)
	{
		cGameCollideScript *pCallback = CollideIt->second;
		
		if(pCallback==NULL) continue;
		if(pCallback->mpEntity==NULL) continue;

		iGameEntity *pEntity = pCallback->mpEntity;
        
		if(pEntity->IsActive() ==false)continue;

		//LogUpdate("  callback %s %s %s\n",pCallback->msFuncName[0].c_str(),pCallback->msFuncName[1].c_str(),pCallback->msFuncName[2].c_str());

		bool bCollide = false;

		for(size_t j=0; j< pEntity->mvBodies.size(); ++j)
		{
			iPhysicsBody *pParentBody = mpCharBody->GetBody();
			iPhysicsBody *pChildBody = pEntity->mvBodies[j];
            
			if(cMath::CheckCollisionBV( *pParentBody->GetBV(),*pChildBody->GetBV()))
			{
				bCollide = pPhysicsWorld->CheckShapeCollision(pParentBody->GetShape(), 
															pParentBody->GetLocalMatrix(),
															pChildBody->GetShape(), 
															pChildBody->GetLocalMatrix(),collideData,1);
			}
													
			if(bCollide) break;
		}

		//Run Collide scripts
		//LogUpdate("  running script");
		if(bCollide)
		{
			if(pCallback->mbCollides)
			{
				if(pCallback->msFuncName[eGameCollideScriptType_During] != "")
				{
					tString sCommand = GetCollideCommand(
						pCallback->msFuncName[eGameCollideScriptType_During],
						"Player", CollideIt->first);
					mpInit->RunScriptCommand(sCommand);
				}
			}
			else
			{
				if(pCallback->msFuncName[eGameCollideScriptType_Enter] != "")
				{
					tString sCommand = GetCollideCommand(
						pCallback->msFuncName[eGameCollideScriptType_Enter],
						"Player", CollideIt->first);
					mpInit->RunScriptCommand(sCommand);
				}

				pCallback->mbCollides = true;
			}
		}
		else
		{
			if(pCallback->mbCollides)
			{
				if(pCallback->msFuncName[eGameCollideScriptType_Leave] != "")
				{
					tString sCommand = GetCollideCommand(
						pCallback->msFuncName[eGameCollideScriptType_Leave],
						"Player", CollideIt->first);
					mpInit->RunScriptCommand(sCommand);
				}

				pCallback->mbCollides = false;
			}
		}
	}
	mbUpdatingCollisionCallbacks = false;

	//////////////////////////////////////////////////
	//Check if any collide script should be deleted.
	//LogUpdate("  Check collide script deleting\n");

	CollideIt = m_mapCollideCallbacks.begin();
	for(; CollideIt != m_mapCollideCallbacks.end(); )
	{
		cGameCollideScript *pCallback = CollideIt->second;
		if(pCallback->mbDeleteMe)
		{
			hplDelete( pCallback );
			m_mapCollideCallbacks.erase(CollideIt++);
		} else {
			++CollideIt;
		}
	}
	
	/////////////////////////////////////////////////
	// Update ground count, this is so that a little air born time still counts as on ground
	if(mpCharBody->IsOnGround()) 
		mlGroundCount = 25;
	else if(mlGroundCount>0)
		mlGroundCount--;

	
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();
	//LogUpdate("  Check For ground\n");
	//////////////////////////////
	//Cast ray and check for ground.
	cVector3f vStart,vEnd;
	
	iCollideShape *pBodyShape = mpCharBody->GetShape();
	vStart = mpCharBody->GetPosition() - cVector3f(0,pBodyShape->GetSize().y/2-0.3f,0);
	vEnd = vStart + cVector3f(0,-0.6f,0);

	mpGroundRayCallback->Clear();
	pPhysicsWorld->CastRay(mpGroundRayCallback,vStart,vEnd,true,false,false);


	//////////////////////////////
	//Update movement
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();
	//LogUpdate("  Movement\n");

	if(mbMoving==false)	
		mvMoveStates[mMoveState]->Stop();

	mvMoveStates[mMoveState]->Update(afTimeStep);
	mpHeadMove->Update(afTimeStep);

	mbMoving = false;

	//////////////////////////////
	//Update camera pos add
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();
	//LogUpdate("  Camera pos\n");
	if(mpCharBody)
	{
		float fYAdd = mfCameraHeightAdd + mpHeadMove->GetPos() + mfHeightAdd + mpDeath->GetHeighAdd() +
										mpInit->mpEffectHandler->GetShakeScreen()->GetScreenAdd().y;
		
		cVector3f vRight = mpCharBody->GetRight();
		float fXAdd =	mpLean->mfMovement * vRight.x +
						mpInit->mpEffectHandler->GetShakeScreen()->GetScreenAdd().x;
		
		float fZAdd =	mpLean->mfMovement * vRight.z+
						mpInit->mpEffectHandler->GetShakeScreen()->GetScreenAdd().x;

		//Log("HEadMove: %f HeightAdd %f Death: %f\n",mpHeadMove->GetPos(),mfHeightAdd,mpDeath->GetHeighAdd());
		
		mpCharBody->SetCameraPosAdd(cVector3f(fXAdd,fYAdd,fZAdd));
	}
	
	///////////////////////////
	//Update state
	
	//Clear picked body
	//mpPushBody = NULL;
	SetPickedBody(NULL);

	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
	lTime = pSystem->GetLowLevel()->GetTime();
	//LogUpdate("  state %d\n",mState);
	if(mpInit->mpInventory->IsActive() ==false && 
		mpInit->mpNotebook->IsActive()==false)
	{
		mvStates[mState]->OnUpdate(afTimeStep);
	}
	//LogUpdate("  took %d ms\n",pSystem->GetLowLevel()->GetTime() - lTime);
}

//-----------------------------------------------------------------------

void cPlayer::Reset()
{
	//Camera
	mpCamera->SetPitchLimits(cVector2f(cMath::ToRad(70),cMath::ToRad(-70) ));
	mpCamera->SetYawLimits(0);
	mpCamera->SetFOV(cMath::ToRad(70));

	cVector2f vScreenSize = mpInit->mpGame->GetGraphics()->GetLowLevel()->GetScreenSize();
	mpCamera->SetAspect(vScreenSize.x / vScreenSize.y);


	//Properties
	mbItemFlash = false;
	mfHealth = 100;
	mbMoving = false;
	mfMass = mfDefaultMass;
	mbLandedFromJump = false;
	mfSpeedMul = 1.0f;
	mfHealthSpeedMul = 1.0f;
	mbActive = true;
	mfPower =0;

	mfHeightAdd =0;

	mfLookSpeed = 1.0f;
	mpCharBody = NULL;
	mpWeaponCallback = NULL;
	mbUpdatingCollisionCallbacks = false;

	mbDamageFromPos = false;

	//jump properties
	mbJumpButtonDown = false;
	mfJumpCount = 0;

	//States
	mMoveState = ePlayerMoveState_Walk;
	mState = ePlayerState_Normal;

	//Crosshair
	mCrossHairState = eCrossHairState_None;
	mvCrossHairPos = cVector2f(400, 300);

	//Stats
	mlStat_NumOfSaves =0;
	
	//Callbacks
	STLMapDeleteAll(m_mapCollideCallbacks);
	
	//Helpers
	mpDeath->Reset();
	mpFlashLight->Reset();
	mpEarRing->Stop(true);
	mpGlowStick->Reset();
	mpFlare->Reset();
	mpLookAt->Reset();
	mpFearFilter->Reset();
	mpLean->Reset();
	mpEarRing->Reset();
	mpHealth->Reset();
	mpHidden->Reset();
	mpGroundRayCallback->Reset();
	if(mpInit->mbHasHaptics) mpHapticCamera->Reset();
}

//-----------------------------------------------------------------------

void cPlayer::OnDraw()
{
	/////////////////////////////////
	// Damage
	mpDamage->Draw();

	/////////////////////////////////
	// Death
	mpDeath->Draw();

	////////////////////////////////////////
	// Noise filter
	mpNoiseFilter->Draw();

	////////////////////////////////////////
	// Fear filter
	mpFearFilter->Draw();

	////////////////////////////////////////
	// Hidden
	mpHidden->Draw();
	
	mpHealth->Draw();
	
	////////////////////////////////
	//Cross hair
	if(IsActive()==false)
	{
		//Do noting...
	}
	else if(mpInit->mbHasHaptics && mpHapticCamera->ShowCrosshair()==false) 
	{
		if(mCrossHairState != eCrossHairState_None)
		{
			cVector3f vProjPos = cMath::MatrixMul(	mpCamera->GetViewMatrix(), 
													mpHapticCamera->GetHandEntity()->GetWorldPosition());
			vProjPos = cMath::MatrixMulDivideW(mpCamera->GetProjectionMatrix(),vProjPos);
			
			cVector2f vPos(	(vProjPos.x+1) * 0.5f, (-vProjPos.y+1)* 0.5f);
			vPos *= cVector2f(800,600);

			vPos += cVector2f(10,10);
			cResourceImage *pImage = mvCrossHairs[mCrossHairState]->GetMaterial()->GetImage(eMaterialTexture_Diffuse);
			cVector2l vSize = pImage->GetSize();
			cVector2f vPosAdd(((float)vSize.x) / 2.0f, ((float)vSize.y) / 2.0f);
			mpGfxDrawer->DrawGfxObject(	mvCrossHairs[mCrossHairState],
										cVector3f(0,0,100)+(vPos - vPosAdd));
		}
	}
	else if(mCrossHairState == eCrossHairState_Item)
	{
		cGfxObject *pObject = mpCurrentItem->GetGfxObject();
		cGfxObject *pAdditive = mpCurrentItem->GetGfxObjectAdditive();
		if(pObject)
		{
			cVector2l vIntSize = pObject->GetMaterial()->GetImage(eMaterialTexture_Diffuse)->GetSize();
			cVector2f vSize((float)vIntSize.x, (float)vIntSize.y);

			cVector2f vPosAdd(((float)vSize.x) / 2.0f, ((float)vSize.y) / 2.0f);
			
			if(mbItemFlash)
			{
				mpGfxDrawer->DrawGfxObject(pObject,cVector3f(0,0,100)+(mvCrossHairPos - vPosAdd),vSize,
											cColor(1,1,1,1));
				for(int i=0; i<2; ++i)
					mpGfxDrawer->DrawGfxObject(pAdditive,cVector3f(0,0,101)+(mvCrossHairPos - vPosAdd),vSize,
											cColor(1,1,1,mItemFlash.val));
				
				/*mpGfxDrawer->DrawGfxObject(pAdditive,cVector3f(3,3,99)+(mvCrossHairPos - vPosAdd),vSize,
											cColor(0,1,0,mItemFlash.val*0.8f));
				mpGfxDrawer->DrawGfxObject(pAdditive,cVector3f(-3,-3,99)+(mvCrossHairPos - vPosAdd),vSize,
											cColor(0,1,0,mItemFlash.val*0.8f));
				mpGfxDrawer->DrawGfxObject(pAdditive,cVector3f(-3,3,99)+(mvCrossHairPos - vPosAdd),vSize,
											cColor(0,1,0,mItemFlash.val*0.8f));
				mpGfxDrawer->DrawGfxObject(pAdditive,cVector3f(3,-3,99)+(mvCrossHairPos - vPosAdd),vSize,
											cColor(0,1,0,mItemFlash.val*0.8f));*/
			}
			else
			{
				mpGfxDrawer->DrawGfxObject(pObject,cVector3f(0,0,100)+(mvCrossHairPos - vPosAdd),vSize,
					cColor(1,0.3f,0.3f,1.0f));
			}
		}
	}
	else if(mCrossHairState != eCrossHairState_None)
	{
		cResourceImage *pImage = mvCrossHairs[mCrossHairState]->GetMaterial()->GetImage(eMaterialTexture_Diffuse);
		cVector2l vSize = pImage->GetSize();
		cVector2f vPosAdd(((float)vSize.x) / 2.0f, ((float)vSize.y) / 2.0f);
		mpGfxDrawer->DrawGfxObject(mvCrossHairs[mCrossHairState],cVector3f(0,0,100)+(mvCrossHairPos - vPosAdd));
	}
	else if(mpInit->mbShowCrossHair)
	{
		cVector3f vPos = cVector3f(400,300,0);
		cResourceImage *pImage = mvCrossHairs[eCrossHairState_Cross]->GetMaterial()->GetImage(eMaterialTexture_Diffuse);
		cVector2l vSize = pImage->GetSize();
		cVector2f vPosAdd(((float)vSize.x) / 2.0f, ((float)vSize.y) / 2.0f);
		mpGfxDrawer->DrawGfxObject(mvCrossHairs[eCrossHairState_Cross],cVector3f(0,0,100)+(vPos - vPosAdd));
	}

	//DEBUG: Memory
	/*float fMbMem = ((float)cMemoryManager::mlTotalMemoryUsage) / (1024.0f * 1024.0f);
	mpFont->Draw(cVector3f(5,5,0),12,cColor(1,1,1,1),eFontAlign_Left,_W("Memory used: %d (%.2f mb)"),
											cMemoryManager::mlTotalMemoryUsage, fMbMem);
	*/

	//DEBUG: Mouse
	//iMouse *pMouse = mpInit->mpGame->GetInput()->GetMouse();
	//mpFont->Draw(cVector3f(5,5,0),12,cColor(1,1,1,1),eFontAlign_Left,_W("Left: %d Right: %d"),
	//													pMouse->ButtonIsDown(eMButton_Left),
	//													pMouse->ButtonIsDown(eMButton_Right));
															

	//DEBUG: State
	if(mpInit->mbHasHaptics)
	{
	/*tWString sState =_W("Unknown");
	if(mState == ePlayerState_Normal) sState = _W("Normal");
	else if(mState == ePlayerState_Push) sState = _W("Push");
	else if(mState == ePlayerState_Move) sState = _W("Move");
	else if(mState == ePlayerState_InteractMode) sState = _W("InteractMode");
	else if(mState == ePlayerState_UseItem) sState = _W("UseItem");
	else if(mState == ePlayerState_Message) sState = _W("Message");
	else if(mState == ePlayerState_Grab) sState = _W("Grab");
	else if(mState == ePlayerState_WeaponMelee) sState = _W("WeaponMelee");
	else if(mState == ePlayerState_Throw) sState = _W("Throw");
	else if(mState == ePlayerState_Climb) sState = _W("Climb");
	
	mpFont->Draw(cVector3f(5,5,0),12,cColor(1,1,1,1),eFontAlign_Left,_W("State: %s"),
					sState.c_str());*/
	}
	//DEBUG: MoveState
	/*tString sState ="";
	if(mMoveState == ePlayerMoveState_Jump) sState = "Jump";
	else if(mMoveState == ePlayerMoveState_Crouch) sState = "Crouch";
	else if(mMoveState == ePlayerMoveState_Walk) sState = "Walk";
	else if(mMoveState == ePlayerMoveState_Run) sState = "Run";
	else if(mMoveState == ePlayerMoveState_Still) sState = "Still";
	
	mpFont->Draw(cVector3f(5,5,0),12,cColor(1,1,1,1),eFontAlign_Left,"MoveState: %s",
					sState.c_str());*/
	
	//DEBUG: Picked body
	/*if(mpInit->mbHasHaptics)
	{
		if(mpPickRayCallback->mpPickedBody){
			mpFont->Draw(cVector3f(5,35,0),12,cColor(1,1,1,1),eFontAlign_Left,_W("Body: %s"),
				cString::To16Char(mpPickRayCallback->mpPickedBody->GetName()).c_str());
		}
		else{
			mpFont->Draw(cVector3f(5,35,0),12,cColor(1,1,1,1),eFontAlign_Left,_W("Body: NULL"));
		}
		mpFont->Draw(cVector3f(5,46,0),12,cColor(1,1,1,1),eFontAlign_Left,_W("Dist: %f"),
			mpPickRayCallback->mfPickedDist);

		tWString sCState = _W("Unknown");
		if(mCrossHairState == eCrossHairState_Inactive)sCState = _W("Inactive");
		if(mCrossHairState == eCrossHairState_Active)sCState = _W("Active");
		if(mCrossHairState == eCrossHairState_Invalid)sCState = _W("Invalid");
		if(mCrossHairState == eCrossHairState_Grab)sCState = _W("Grab");
		if(mCrossHairState == eCrossHairState_Examine)sCState = _W("Examine");
		if(mCrossHairState == eCrossHairState_Pointer)sCState = _W("Pointer");
		if(mCrossHairState == eCrossHairState_Item)sCState = _W("Item");
		if(mCrossHairState == eCrossHairState_DoorLink)sCState = _W("DoorLink");
		if(mCrossHairState == eCrossHairState_PickUp)sCState = _W("PickUp");
		if(mCrossHairState == eCrossHairState_Ladder)sCState = _W("Ladder");
		if(mCrossHairState == eCrossHairState_None)sCState = _W("None");
		mpFont->Draw(	cVector3f(5,66,0),12,cColor(1,1,1,1),eFontAlign_Left,
						_W("CState: %s"),sCState.c_str());

	}*/

	//DEBUG: On ground and step material
	/*mpFont->Draw(cVector3f(5,17,0),12,cColor(1,1,1,1),eFontAlign_Left,"Position: %f ClimbPos: %f ClimbCount: %f\n",
											mpHeadMove->GetPosition(),
											mpHeadMove->GetClimbPosition(),
											mpHeadMove->GetClimbCount());*/
	//mpFont->Draw(cVector3f(5,29,0),12,cColor(1,1,1,1),eFontAlign_Left,"Gravity: %f",
	//mpCharBody->GetForceVelocity().y);
	//mpFont->Draw(cVector3f(5,29,0),12,cColor(1,1,1,1),eFontAlign_Left,"CameraPos: %s",
	//	mpCamera->GetPosition().ToString().c_str());
	//mpFont->Draw(cVector3f(5,43,0),12,cColor(1,1,1,1),eFontAlign_Left,"CharPos: %s",
	//	GetCharacterBody()->GetPosition().ToString().c_str());

	/*if(mpRayCallback->mpMaterial)
	mpFont->Draw(cVector3f(5,17,0),12,cColor(1,1,1,1),eFontAlign_Left,"Material: %s",
	mpGroundRayCallback->mpMaterial->GetName().c_str());*/

	//DEBUG: health
	if(mbShowHealth)
	{
		mpFont->Draw(cVector3f(5,5,0),12,cColor(1,1,1,1),eFontAlign_Left,_W("Health: %.0f"),
								mfHealth);
	}
	
	//DEBUG: misc
	//mpFont->Draw(cVector3f(5,20,0),12,cColor(1,1,1,1),eFontAlign_Left,
	//			_W("Ground: %d Speed: %f ForceSpeed: %f"),
	//													mpCharBody->IsOnGround()?1:0,
	//													mpCharBody->GetMoveSpeed(eCharDir_Forward),
	//													mpCharBody->GetForceVelocity().Length()	
	//												);
	//cVector3f vGravity = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld()->GetGravity();
	//mpFont->Draw(cVector3f(5,20,0),12,cColor(1,1,1,1),eFontAlign_Left,"Gravity: %s",
	//														vGravity.ToString().c_str());
				
	//DEBUG: sounds playing
	if(mbShowSoundsPlaying)
	{
		tStringVec vSoundNames;
		std::vector<cSoundEntry*> vEntries;

		//////////////////////////////
		//Sound channels
		tSoundEntryList *pEntryList = mpInit->mpGame->GetSound()->GetSoundHandler()->GetWorldEntryList();
		for(tSoundEntryListIt it = pEntryList->begin(); it != pEntryList->end();++it)
		{
			iSoundChannel *pSound = it->mpSound;
			vSoundNames.push_back(pSound->GetData()->GetName());
			vEntries.push_back(&(*it));
		}
		
		vSoundNames.push_back("");
		vEntries.push_back(NULL);

		pEntryList = mpInit->mpGame->GetSound()->GetSoundHandler()->GetGuiEntryList();
		for(tSoundEntryListIt it = pEntryList->begin(); it != pEntryList->end();++it)
		{
			iSoundChannel *pSound = it->mpSound;
			vSoundNames.push_back(pSound->GetData()->GetName());
			vEntries.push_back(&(*it));
		}

		mpFont->Draw(cVector3f(5,18,0),10,cColor(1,1,1,1),eFontAlign_Left,_W("Num of sounds: %d"),vSoundNames.size()-1);
		
		int lRow=0, lCol=0;
		for(int i=0; i< (int)vSoundNames.size(); i++)
		{
			cSoundEntry *pEntry = vEntries[i];
			if(pEntry == NULL){
				lRow = 4;
				lCol =0;
				continue;
			}
			mpFont->Draw(cVector3f((float)lCol*250,26+(float)lRow*11,0),10,cColor(1,1,1,1),eFontAlign_Left,
				_W("%ls(%.2f (%.2f %.2f)->%.2f"),cString::To16Char(vSoundNames[i]).c_str(),
				//_W("%ls(%.2f (%.2f %.2f)->%.2f %d %.2f/%.2f)"),cString::To16Char(vSoundNames[i]).c_str(),
				pEntry->mpSound->GetVolume(),
								pEntry->mfNormalVolumeMul,
								pEntry->mfNormalVolumeFadeSpeed,
								pEntry->mfNormalVolumeFadeDest,
								pEntry->mpSound->GetPriority(),
								pEntry->mpSound->GetElapsedTime(),
								pEntry->mpSound->GetTotalTime()
								
								);
//								pEntry->mpSound->GetPriority(),
//								pEntry->mpSound->IsBufferUnderrun()?1:0);
			
			lCol++;
			if(lCol == 3)
			{
				lCol =0;
				lRow++;
			}
		}
		
		//////////////////////////////
		//Music
        cMusicEntry *pMusic = mpInit->mpGame->GetSound()->GetMusicHandler()->GetCurrentSong();
		if(pMusic){
				iSoundChannel *pChannel = pMusic->mpStream;
				mpFont->Draw(cVector3f(5,18+70,0),10,cColor(1,1,1,1),eFontAlign_Left,
					_W("Music: '%ls' vol: %.2f playing: %d prio: %d elapsed: %.2f total time: %.2f"),
						cString::To16Char(pChannel->GetData()->GetName()).c_str(),
						pChannel->GetVolume(),
						pChannel->IsPlaying(),
						pChannel->GetPriority(),
						pChannel->GetElapsedTime(),
						pChannel->GetTotalTime()
						);

		}
		        
	}
	
	//DEBUG: Portals
	/*tString sPortals = "Portals: ";
	cPortalContainer *pContainer = mpInit->mpGame->GetScene()->GetWorld3D()->GetPortalContainer();
    tStringList* pStringList = pContainer->GetVisibleSectorsList();
	for(tStringListIt it=pStringList->begin(); it != pStringList->end(); ++it)
	{
		sPortals += *it + ", ";
	}

	mpFont->Draw(cVector3f(5,5,0),12,cColor(1,1,1,1),eFontAlign_Left,"%s",
															sPortals.c_str());*/
	
	mvStates[mState]->OnDraw();
}

void cPlayer::OnPostSceneDraw()
{
	cCamera3D *pCam = static_cast<cCamera3D*>(mpScene->GetCamera());
	iLowLevelGraphics *pLowGfx = mpInit->mpGame->GetGraphics()->GetLowLevel();
	pLowGfx->SetMatrix(eMatrix_ModelView, pCam->GetViewMatrix());

	pLowGfx->SetTexture(0,NULL);
	pLowGfx->SetBlendActive(false);

	/*mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawLine(mvLineStart,mvLineEnd,cColor(1,1,1,1));
	mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawSphere(mvLineStart,0.1f,cColor(1,0,1,1));
	mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawSphere(mvLineEnd,0.1f,cColor(1,0,1,1));*/
	
	mpFlashLight->OnPostSceneDraw();

	mvStates[mState]->OnPostSceneDraw();

	///////////////////////////////
	//Gui Hand effects
	if(mpInit->mbHasHaptics)
	{
		mpHapticCamera->OnPostSceneDraw();
	}
}

//////////////////////////////////////////////////////////////////////////
// SAVING
//////////////////////////////////////////////////////////////////////////

//-------------------------------------------------------------------

void cPlayer::AddSaveData(cSavedWorld* apSavedWorld)
{
	//Collide callbacks
	tGameCollideScriptMapIt colIt = m_mapCollideCallbacks.begin();
	for(; colIt != m_mapCollideCallbacks.end(); ++colIt)
	{
		cGameCollideScript *pScript = colIt->second;
		cSaveGame_cGameCollideScript savedScript;
		savedScript.LoadFrom(pScript);

		apSavedWorld->mlstCollideCallbacks.Add(savedScript);
	}
}

//-------------------------------------------------------------------

void cPlayer::LoadSaveData(cSavedWorld* apSavedWorld)
{
	//Collide callbacks
	//Collide scripts
	cContainerListIterator<cSaveGame_cGameCollideScript> colIt = apSavedWorld->mlstCollideCallbacks.GetIterator();
	while(colIt.HasNext())
	{
		cSaveGame_cGameCollideScript &savedScript = colIt.Next();
		cGameCollideScript *pCallback = hplNew( cGameCollideScript, () );

		pCallback->mpEntity = mpInit->mpMapHandler->GetGameEntity(savedScript.msEntity);
		if(pCallback->mpEntity==NULL)
		{
			Warning("Couldn't find entity '%s'\n",savedScript.msEntity.c_str());
			hplDelete( pCallback );
			continue;
		}
		savedScript.SaveTo(pCallback);


		m_mapCollideCallbacks.insert(tGameCollideScriptMap::value_type(savedScript.msEntity,pCallback));
	}
}

//-------------------------------------------------------------------

void cPlayer::SaveToGlobal(cPlayer_GlobalSave *apSave)
{
	cPlayer_GlobalSave *pData = apSave;
	
	//////////////////////////////
	//Stats
	kSaveData_SaveTo(mlStat_NumOfSaves);

	//////////////////////////////
	//Global
	kSaveData_SaveTo(mfForwardUpMul);
	kSaveData_SaveTo(mfForwardRightMul);

	kSaveData_SaveTo(mfUpMul);
	kSaveData_SaveTo(mfRightMul);

	kSaveData_SaveTo(mbPickAtPoint);
	kSaveData_SaveTo(mbRotateWithPlayer);
	kSaveData_SaveTo(mbUseNormalMass);

	kSaveData_SaveTo(mfGrabMassMul);

	//////////////////////////////
	//Private
	kSaveData_SaveTo(mbActive);

	kSaveData_SaveTo(mfHeightAdd);

	kSaveData_SaveTo(mfSpeedMul);
	kSaveData_SaveTo(mfHealthSpeedMul);
	kSaveData_SaveTo(mfHeadMoveSizeMul);
	kSaveData_SaveTo(mfHeadMoveSpeedMul);

	kSaveData_SaveTo(mState);
	kSaveData_SaveTo(mMoveState);
	kSaveData_SaveTo(mCrossHairState);

	kSaveData_SaveTo(mbItemFlash);
	kSaveData_SaveTo(mfHealth);
	kSaveData_SaveTo(mfPower);
	kSaveData_SaveTo(mfMass);

	///////////////////////////////////////
	//Particles on camera
	cNode3D *pNode = mpCamera->GetAttachmentNode();

	pData->mvOnCameraPS.Clear();
	cEntityIterator it = pNode->GetEntityIterator();
	while(it.HasNext())
	{
		iEntity3D *pEntity3D = static_cast<iEntity3D*>(it.Next());
		if(pEntity3D->GetEntityType() == "ParticleSystem3D")
		{
			cParticleSystem3D *pPS = static_cast<cParticleSystem3D *>(pEntity3D);

			cPlayer_GlobalSave_CameraPS cameraPS;
			cameraPS.msName = pPS->GetName();
			cameraPS.msFile = pPS->GetDataName();
			pData->mvOnCameraPS.Add(cameraPS);
		}
	}

	//////////////////////////////
	// Lights
	apSave->mbFlashlightActive = mpFlashLight->IsActive();
	apSave->mbFlashlightDisabled = mpFlashLight->IsDisabled();
	apSave->mbGlowstickActive = mpGlowStick->IsActive();
	apSave->mbFlareActive = mpFlare->IsActive();
	apSave->mfFlareTime = mpFlare->GetTime();

	//////////////////////////////
	//Body and Camera Specific
	pData->mvPosition = mpCharBody->GetPosition();
	pData->mfYaw = mpCharBody->GetYaw();
	pData->mfPitch =mpCamera->GetPitch();
}

//-------------------------------------------------------------------

void cPlayer::LoadFromGlobal(cPlayer_GlobalSave *apSave)
{
	cPlayer_GlobalSave *pData = apSave;

	//////////////////////////////
	//Stats
	kSaveData_LoadFrom(mlStat_NumOfSaves);
	
	//////////////////////////////
	//Global
	kSaveData_LoadFrom(mfForwardUpMul);
	kSaveData_LoadFrom(mfForwardRightMul);

	kSaveData_LoadFrom(mfUpMul);
	kSaveData_LoadFrom(mfRightMul);

	kSaveData_LoadFrom(mbPickAtPoint);
	kSaveData_LoadFrom(mbRotateWithPlayer);
	kSaveData_LoadFrom(mbUseNormalMass);

	kSaveData_LoadFrom(mfGrabMassMul);

	//////////////////////////////
	//Private
	SetActive(pData->mbActive);

	kSaveData_LoadFrom(mfHeightAdd);

	//Probably better of skipping these:
	/*kSaveData_LoadFrom(mfSpeedMul);
	kSaveData_LoadFrom(mfHealthSpeedMul);
	kSaveData_LoadFrom(mfHeadMoveSizeMul);
	kSaveData_LoadFrom(mfHeadMoveSpeedMul);*/ 
	
	//Skip these for now.
	ChangeMoveState(apSave->mMoveState,true);
	//kSaveData_LoadFrom(mState);
	//kSaveData_LoadFrom(mMoveState);
	//kSaveData_LoadFrom(mCrossHairState);

	kSaveData_LoadFrom(mbItemFlash);
	kSaveData_LoadFrom(mfHealth);
	kSaveData_LoadFrom(mfPower);
	//kSaveData_LoadFrom(mfMass); //Skip this

	///////////////////////////////////////
	//Particles on camera
	cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
	for(size_t i=0;i<pData->mvOnCameraPS.Size(); ++i)
	{
		cParticleSystem3D *pPS = pWorld->CreateParticleSystem(
												pData->mvOnCameraPS[i].msName,
												pData->mvOnCameraPS[i].msFile,
												1,cMatrixf::Identity);
		if(pPS)
		{
			mpCamera->AttachEntity(pPS);
		}
	}

	//////////////////////////////
	// Lights
	if(apSave->mbFlashlightActive) mpFlashLight->SetActive(true);
	if(apSave->mbFlashlightDisabled) mpFlashLight->SetDisabled(true);
	if(apSave->mbGlowstickActive) mpGlowStick->SetActive(true);
	if(apSave->mbFlareActive){
		mpFlare->SetActive(true);
		mpFlare->SetTime(apSave->mfFlareTime);
	}


	//////////////////////////////
	//Body and Camera Specific
	mpCharBody->SetPosition(pData->mvPosition);
	
	mpCharBody->SetYaw(pData->mfYaw);
	mpCamera->SetYaw(pData->mfYaw);
	mpCamera->SetPitch(pData->mfPitch);
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// SAVE OBJECT STUFF
//////////////////////////////////////////////////////////////////////////

//-------------------------------------------------------------------

kBeginSerializeBase(cSaveData_cPlayer)
kSerializeVar(mlStat_NumOfSaves,eSerializeType_Int32)
kSerializeClassContainer(mlstCollideCallbacks,cSaveGame_cGameCollideScript, eSerializeType_Class)
kEndSerialize()

//-----------------------------------------------------------------------

iSaveObject* cSaveData_cPlayer::CreateSaveObject(cSaveObjectHandler *apSaveObjectHandler,cGame *apGame)
{
	cContainerListIterator<cSaveGame_cGameCollideScript> it = mlstCollideCallbacks.GetIterator();
	while(it.HasNext())
	{
		cSaveGame_cGameCollideScript& saveScript = it.Next();

		iGameEntity* pEntity = gpInit->mpMapHandler->GetGameEntity(saveScript.msEntity);
		if(pEntity==NULL){
			Error("Couldn't find game entity '%s'\n",saveScript.msEntity.c_str());
			continue;
		}

		cGameCollideScript *pCallback = hplNew( cGameCollideScript, () );
		pCallback->mpEntity = pEntity;
		saveScript.SaveTo(pCallback);

		gpInit->mpPlayer->m_mapCollideCallbacks.insert(tGameCollideScriptMap::value_type(saveScript.msEntity,pCallback));
	}

	return NULL;
}

//-----------------------------------------------------------------------

int cSaveData_cPlayer::GetSaveCreatePrio()
{
	return 10;
}


//-----------------------------------------------------------------------

iSaveData* cPlayer::CreateSaveData()
{
	cSaveData_cPlayer *pData = hplNew( cSaveData_cPlayer, () );
	
	//Collide callbacks
	{
		tGameCollideScriptMapIt it = m_mapCollideCallbacks.begin();
		for(; it != m_mapCollideCallbacks.end(); ++it)
		{
			cGameCollideScript *pScript = it->second;
			cSaveGame_cGameCollideScript saveScript;
			saveScript.LoadFrom(pScript);
			pData->mlstCollideCallbacks.Add(saveScript);
		}
	}
	
	return pData;
}

//-----------------------------------------------------------------------
